﻿Imports System.IO
Imports System.Text

''' <summary>
''' Poging om TPS-formaat te reverse engineeren
''' </summary>
Public Class ImportTpsDal

    Public Shared Function GetImportDataSet(ByVal bestandsnaam As String) As ImportDataSet
        Dim fi As New FileInfo(bestandsnaam)
        Dim bytes(Convert.ToInt32(fi.Length)) As Byte
        Dim fs As New FileStream(bestandsnaam, FileMode.Open, FileAccess.Read)

        ' BinaryFormatter - Deserialize - <Serializable()>

        fs.Read(bytes, 0, bytes.Length)

        Dim ds As New ImportDataSet()

        Dim pos As Integer = FindSequence(bytes, pos, "Partner: ")
        While pos >= 0
            AddArtikel(ds, bytes, pos)
            pos = FindSequence(bytes, pos, "Partner: ")
        End While

        Return ds
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="bytes"></param>
    ''' <param name="startFrom"></param>
    ''' <param name="sequence"></param>
    ''' <returns>Index van de positie vlak na de gevonden sequence</returns>
    ''' <remarks></remarks>
    Private Shared Function FindSequence(ByVal bytes() As Byte, ByVal startFrom As Integer, ByVal sequence As String) As Integer
        Dim ndx As Integer = 0
        For pos As Integer = startFrom To bytes.Length - 1
            If (bytes(pos) = Convert.ToByte(sequence.Chars(ndx))) Then
                If (ndx = sequence.Length - 1) Then
                    ' sequence gevonden
                    Return pos + 1
                Else
                    ndx += 1
                End If
            Else
                ndx = 0
            End If
        Next
        Return -1
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="bytes"></param>
    ''' <param name="startFrom"></param>
    ''' <param name="sequence1"></param>
    ''' <param name="sequence2"></param>
    ''' <returns>Index van de positie vlak na de gevonden sequence</returns>
    ''' <remarks></remarks>
    Private Shared Function FindSequence(ByVal bytes() As Byte, ByVal startFrom As Integer, ByVal sequence1 As String, ByVal sequence2 As String) As Integer
        Dim ndx1 As Integer = 0
        Dim ndx2 As Integer = 0
        For pos As Integer = startFrom To bytes.Length - 1
            If (bytes(pos) = Convert.ToByte(sequence1.Chars(ndx1))) Then
                If (ndx1 = sequence1.Length - 1) Then
                    ' sequence1 gevonden
                    Return pos + 1
                Else
                    ndx1 += 1
                End If
            Else
                ndx1 = 0
            End If
            If (bytes(pos) = Convert.ToByte(sequence2.Chars(ndx2))) Then
                If (ndx2 = sequence2.Length - 1) Then
                    ' sequence2 gevonden
                    Return pos + 1
                Else
                    ndx2 += 1
                End If
            Else
                ndx2 = 0
            End If
        Next
        Return -1
    End Function

    Private Shared Sub AddArtikel(ByVal ds As ImportDataSet, ByVal bytes() As Byte, ByVal pos As Integer)
        Try
            While True
                ' zoek minstens 10 BO-codes na mekaar
                While bytes(pos) <> &HB0
                    pos += 1
                End While
                Dim beginB0Codes As Integer = pos
                While bytes(pos) = &HB0
                    pos += 1
                End While
                If (pos - beginB0Codes > 10) Then Exit While
            End While
            pos += 32 ' ga door naar positie van ArtikelCode
            If (bytes(pos) >= 127) Then Return

            Dim dummyString As String = String.Empty
            Dim dummyDecimal As String = String.Empty

            Dim row As ImportDataSet._Blad1_Row = ds._Blad1_.New_Blad1_Row()
            row.ArtikelCode = String.Empty ' anders krijg je DbNull error
            row.ArtikelOmschrijving_NL = String.Empty
            row.Barcode = String.Empty
            row.AP_Excl = "0"

            pos += ExtractCStringField(bytes, pos, row.ArtikelCode)
            pos += ExtractCStringField(bytes, pos, dummyString) ' volgens TopScan is dit een barcode ???
            pos += ExtractCStringField(bytes, pos, row.ArtikelOmschrijving_NL)
            pos += ExtractCStringField(bytes, pos, dummyString) ' ArtikelOmschrijvingFR
            pos += ExtractCStringField(bytes, pos, dummyString) ' ArtikelOmschrijvingEN
            pos += ExtractCStringField(bytes, pos, dummyString) ' ArtikelOmschrijvingDU
            pos += 31
            pos += ExtractCStringField(bytes, pos, row.Barcode)
            pos = FindSequence(bytes, pos, "EUR", "1  ")
            pos += 1
            If (Not DecimalZeroValue(bytes(pos))) Then
                pos += ExtractDecimalField(bytes, pos, dummyDecimal)
                pos += ExtractDecimalField(bytes, pos, row.AP_Excl)
            End If

            ds._Blad1_.Add_Blad1_Row(row)
            'ds._Blad1_.Add_Blad1_Row(artikelCode, "omschr", "lev", "cat", "subcat", "barcode", "aantal", "AP_Excl", "AP_Incl", "VP_Excl", "VP_Incl", "BTW")
        Catch ex As Exception
            log.WriteError(ex.Message)
        End Try
    End Sub

    Private Shared Function ExtractCStringField(ByVal bytes() As Byte, ByVal pos As Integer, ByRef field As String) As Integer
        Dim l As Integer = pos
        While bytes(l) <> 0
            l += 1
        End While
        field = Encoding.UTF8.GetString(bytes, pos, l - pos)
        Return l - pos + 1
    End Function

    Private Shared Function ExtractDecimalField(ByVal bytes() As Byte, ByVal pos As Integer, ByRef field As String) As Integer
        Try
            Dim format As Integer = bytes(pos) * 256 + bytes(pos + 1)
            Select Case format
                Case &H502
                    If (bytes(pos + 3) <> 0) Then Throw New Exception("ExtractDecimalField: bij 0502 wordt slechts 1 byte verwacht")
                    field = String.Format("-0.{0}", ExtractByte(bytes(pos + 2)))
                    Return 4
                Case &H503, &H903, &H70D
                    If (bytes(pos + 4) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " worden slechts 2 bytes verwacht")
                    field = String.Format("{0}.{1}", ExtractByte(bytes(pos + 2)), ExtractByte(bytes(pos + 3)))
                    Return 5
                Case &H602, &HA02
                    If (bytes(pos + 3) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " wordt slechts 1 byte verwacht")
                    field = String.Format("0.{0}", ExtractByte(bytes(pos + 2)))
                    Return 4
                Case &H509, &H303
                    If (bytes(pos + 4) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " worden slechts 2 bytes verwacht")
                    field = String.Format("0.{0}{1}", ExtractByte(bytes(pos + 2)), ExtractByte(bytes(pos + 3)))
                    Return 5
                Case &H604, &H804
                    If (bytes(pos + 5) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " worden 3 bytes verwacht")
                    field = String.Format("0.{0}{1}{2}", ExtractByte(bytes(pos + 2)), ExtractByte(bytes(pos + 3)), ExtractByte(bytes(pos + 4)))
                    Return 6
                Case &H6404
                    If (bytes(pos + 5) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " worden 3 bytes verwacht")
                    field = String.Format("{0}{1}.{2}", ExtractByte(bytes(pos + 2)), ExtractByte(bytes(pos + 3)), ExtractByte(bytes(pos + 4)))
                    Return 6
                Case &H505, &H705
                    If (bytes(pos + 6) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " worden 4 bytes verwacht")
                    field = String.Format("{0}.{1}{2}{3}", ExtractByte(bytes(pos + 2)), ExtractByte(bytes(pos + 3)), _
                                ExtractByte(bytes(pos + 4)), ExtractByte(bytes(pos + 5)))
                    Return 7
                Case &HF000
                    If (bytes(pos + 2) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " wordt 00 verwacht")
                    If (bytes(pos + 6) <> 0) Then Throw New Exception("ExtractDecimalField: bij " & format & " worden 3 bytes verwacht")
                    field = String.Format("-{0}.{1}{2}", ExtractByte(bytes(pos + 3)), ExtractByte(bytes(pos + 4)), ExtractByte(bytes(pos + 5)))
                    Return 7
                Case Else
                    Throw New Exception("ExtractDecimalField: formaat " & format & " nog niet voorzien")
            End Select
        Catch ex As Exception
            log.WriteError(ex.Message)
            Throw
        End Try
    End Function

    Private Shared Function ExtractByte(ByVal b As Byte) As Integer
        Return (b And &HF0) * 10 + (b And &HF)
    End Function

    Private Shared Function DecimalZeroValue(ByVal b As Byte) As Boolean
        Return b = &HF5 OrElse b = &H65 OrElse b = &H66
    End Function

End Class